<!DOCTYPE html>
<html>
<head>
<title>DAY01</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */

/* RESET
=============================================================================*/

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
}

/* BODY
=============================================================================*/

body {
  font-family: Helvetica, arial, freesans, clean, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: #333;
  background-color: #fff;
  padding: 20px;
  max-width: 960px;
  margin: 0 auto;
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
  margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
  font-size: inherit;
}

h1 {
  font-size: 28px;
  color: #000;
}

h2 {
  font-size: 24px;
  border-bottom: 1px solid #ccc;
  color: #000;
}

h3 {
  font-size: 18px;
}

h4 {
  font-size: 16px;
}

h5 {
  font-size: 14px;
}

h6 {
  color: #777;
  font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
  margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
  color: #4183C4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
  padding-left: 30px;
}

ul li > :first-child, 
ol li > :first-child, 
ul li ul:first-of-type, 
ol li ol:first-of-type, 
ul li ol:first-of-type, 
ol li ul:first-of-type {
  margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
  margin-bottom: 0;
}

dl {
  padding: 0;
}

dl dt {
  font-size: 14px;
  font-weight: bold;
  font-style: italic;
  padding: 0;
  margin: 15px 0 5px;
}

dl dt:first-child {
  padding: 0;
}

dl dt>:first-child {
  margin-top: 0px;
}

dl dt>:last-child {
  margin-bottom: 0px;
}

dl dd {
  margin: 0 0 15px;
  padding: 0 15px;
}

dl dd>:first-child {
  margin-top: 0px;
}

dl dd>:last-child {
  margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
  font-size: 12px;
  font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
  margin: 0 0px;
  padding: 0px 0px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px;
}

pre>code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

pre {
  background-color: #f8f8f8;
  border: 1px solid #ccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

pre code, pre tt {
  background-color: transparent;
  border: none;
}

kbd {
    -moz-border-bottom-colors: none;
    -moz-border-left-colors: none;
    -moz-border-right-colors: none;
    -moz-border-top-colors: none;
    background-color: #DDDDDD;
    background-image: linear-gradient(#F1F1F1, #DDDDDD);
    background-repeat: repeat-x;
    border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
    border-image: none;
    border-radius: 2px 2px 2px 2px;
    border-style: solid;
    border-width: 1px;
    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
    line-height: 10px;
    padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
  border-left: 4px solid #DDD;
  padding: 0 15px;
  color: #777;
}

blockquote>:first-child {
  margin-top: 0px;
}

blockquote>:last-child {
  margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
  clear: both;
  margin: 15px 0;
  height: 0px;
  overflow: hidden;
  border: none;
  background: transparent;
  border-bottom: 4px solid #ddd;
  padding: 0;
}

/* TABLES
=============================================================================*/

table th {
  font-weight: bold;
}

table th, table td {
  border: 1px solid #ccc;
  padding: 6px 13px;
}

table tr {
  border-top: 1px solid #ccc;
  background-color: #fff;
}

table tr:nth-child(2n) {
  background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
  max-width: 100%
}
</style>
</head>
<body>
<h3>1. 分析项目</h3>
<p>当需要开发某个项目时，首先，应该分析这个项目中，需要处理哪些种类的数据！例如：用户、商品、商品类别、收藏、订单、购物车、收货地址……</p>
<p>然后，将以上这些种类的数据的处理排个顺序，即先处理哪种数据，后处理哪种数据！通常，应该先处理基础数据，再处理所相关的数据，例如需要先处理商品数据，才可以处理订单数据，如果多种数据之间没有明显的关联，则应该先处理简单的，再处理较难的！则以上这些数据的处理顺序应该是：用户 &gt; 收货地址 &gt; 商品类别 &gt; 商品 &gt; 收藏 &gt; 购物车 &gt; 订单。</p>
<p>当确定了数据处理顺序后，就应该分析某个用户对应的功能有哪些，以“用户”数据为例，相关功能有：注册、登录、修改密码、修改资料、上传头像……</p>
<p>然后，还是需要确定以上功能的开发顺序，通常，遵循“增 &gt; 查 &gt; 删 &gt; 改”的顺序，则以上功能的开发顺序应该是：注册 &gt; 登录 &gt; 修改密码 &gt; 修改资料 &gt; 上传头像。</p>
<p>每个功能的开发都应该遵循“创建数据表 &gt; 创建实体类 &gt; 持久层 &gt; 业务层 &gt; 控制器层 &gt; 前端页面”。</p>
<p><strong>一次只解决一个问题！</strong></p>
<h3>2. 用户-注册-创建数据表</h3>
<p>首先，需要创建数据库：</p>
<pre><code>CREATE DATABASE tedu_store;
</code></pre>

<p>然后，使用该数据库：</p>
<pre><code>USE tedu_store;
</code></pre>

<p>再创建数据表：</p>
<pre><code>CREATE TABLE t_user (
    uid INT AUTO_INCREMENT COMMENT '用户id',
    username VARCHAR(20) UNIQUE NOT NULL COMMENT '用户名',
    password CHAR(32) NOT NULL COMMENT '密码',
    salt CHAR(36) COMMENT '盐值',
    gender INT COMMENT '性别：0-女，1-男',
    phone VARCHAR(20) COMMENT '电话号码',
    email VARCHAR(50) COMMENT '邮箱',
    avatar VARCHAR(50) COMMENT '头像',
    is_delete INT COMMENT '是否标记为删除：0-未删除，1-已删除',
    created_user VARCHAR(20) COMMENT '创建人',
    created_time DATETIME COMMENT '创建时间',
    modified_user VARCHAR(20) COMMENT '最后修改人',
    modified_time DATETIME COMMENT '最后修改时间',
    PRIMARY KEY (uid)
) DEFAULT CHARSET=UTF8;
</code></pre>

<h3>3. 用户-注册-创建实体类</h3>
<p>首先，从FTP服务器下载项目的压缩包，通过<strong>Import</strong> &gt; <strong>Existing Maven Projects</strong>导入项目。</p>
<p>由于项目中添加了数据库相关依赖，在启动时，SpringBoot就会读取相关配置，如果没有，则报错，所以，就需要将此前项目中的配置复制到此项目中，注意修改数据库的名称为<code>tedu_store</code>：</p>
<pre><code>spring.datasource.url=jdbc:mysql://localhost:3306/tedu_store?useUnicode=true&amp;characeterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

mybatis.mapper-locations=classpath:mappers/*.xml
</code></pre>

<p>然后，在单元测试类中，先测试运行默认存在的空方法，测试通过后，自行编写并测试数据库连接是否正确：</p>
<pre><code>@Autowired
DataSource dataSource;

@Test
public void getConnection() throws SQLException {
    Connection conn = dataSource.getConnection();
    System.err.println(conn);
}
</code></pre>

<p>接下来，应该创建实体类，在项目中，后续还会出现更多的数据表，而每张表中都会有<code>created_user</code>等日志字段，对应的每个实体类中也需要有这些属性，为了避免重复声明这些属性，可以先创建<code>cn.tedu.store.entity.BaseEntity</code>实体类的基类：</p>
<pre><code>abstract class BaseEntity implements Serializable {
    private String createdUser;
    private Date createdTime;
    private String modifiedUser;
    private Date modifiedTime;

    // SET/GET
}
</code></pre>

<p>然后再<code>cn.tedu.store.entity.User</code>实体类，继承自以上基类：</p>
<pre><code>public class User extends BaseEntity {

    private Integer uid;
    private String username;
    private String password;
    private String salt;
    private Integer gender;
    private String phone;
    private String email;
    private String avatar;
    private Integer isDelete;

    // SET/GET/toString/equals和hashCode
}
</code></pre>

<h3>4. 用户-注册-持久层</h3>
<p><strong>4.1. 规划SQL语句</strong></p>
<p>注册的本质是向数据表中插入新的数据，则执行的SQL语句应该是：</p>
<pre><code>insert into t_user (除了uid以外的字段列表) values (匹配的User类中属性名的占位符)
</code></pre>

<p>通常，用户注册时，不允许使用已经存在的用户名，即用户名都是唯一的，这一点应该在Java程序中进行检查，而不应该直接将数据送到数据库服务器尝试执行插入！所以，可以根据用户尝试注册的用户名查询用户数据，如果查询结果为null，则表示用户名没有被占用，可以正常注册，如果查询到了结果，则表示用户名已经被占用，则不会执行插入数据！此次查询时需要执行的SQL语句大致是：</p>
<pre><code>select uid from t_user where username=?
</code></pre>

<p><strong>4.2. 接口与抽象方法</strong></p>
<p>首先，应该在启动类之前添加<code>@MapperScan(&quot;cn.tedu.store.mapper&quot;)</code>以配置持久层接口所在的包。</p>
<p>创建<code>cn.tedu.store.mapper.UserMapper</code>持久层接口，并在接口中添加抽象方法：</p>
<pre><code>Integer insert(User user);

User findByUsername(String username);
</code></pre>

<p><strong>4.3. 配置映射</strong></p>
<p>检查在<strong>application.properties</strong>中是否配置了映射文件的位置：</p>
<pre><code>mybatis.mapper-locations=classpath:mappers/*.xml
</code></pre>

<p>然后，在<strong>src/main/resources</strong>下创建名为<strong>mappers<strong>的文件夹，在该文件夹中添加</strong>UserMapper.xml</strong>文件（从前序项目中复制即可），并配置该文件：</p>
<pre><code>&lt;mapper namespace=&quot;cn.tedu.store.mapper.UserMapper&quot;&gt;

    &lt;!-- 插入用户数据 --&gt;
    &lt;!-- Integer insert(User user) --&gt;
    &lt;insert id=&quot;insert&quot;
        useGeneratedKeys=&quot;true&quot;
        keyProperty=&quot;uid&quot;&gt;
        INSERT INTO t_user (
            username, password,
            salt, gender,
            phone, email,
            avatar, is_delete,
            created_user, created_time,
            modified_user, modified_time
        ) VALUES (
            #{username}, #{password},
            #{salt}, #{gender},
            #{phone}, #{email},
            #{avatar}, #{isDelete},
            #{createdUser}, #{createdTime},
            #{modifiedUser}, #{modifiedTime}
        )
    &lt;/insert&gt;

    &lt;!-- 根据用户名查询用户数据 --&gt;
    &lt;!-- User findByUsername(String username) --&gt;
    &lt;select id=&quot;findByUsername&quot;
        resultType=&quot;cn.tedu.store.entity.User&quot;&gt;
        SELECT 
            uid
        FROM 
            t_user 
        WHERE 
            username=#{username}
    &lt;/select&gt;

&lt;/mapper&gt;
</code></pre>

<p>完成后，在<strong>src/test/java</strong>下创建<code>cn.tedu.store.mapper.UserMapperTests</code>用户数据持久层的测试类，并在类中编写并执行单元测试：</p>
<pre><code>@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTests {

    @Autowired
    private UserMapper mapper;  

    @Test
    public void insert() {
        User user = new User();
        user.setUsername(&quot;root&quot;);
        user.setPassword(&quot;1234&quot;);
        Integer rows = mapper.insert(user);
        System.err.println(&quot;rows=&quot; + rows);
    }

    @Test
    public void findByUsername() {
        String username = &quot;root&quot;;
        User user = mapper.findByUsername(username);
        System.err.println(user);
    }

}
</code></pre>

<h3>5. 用户-注册-业务层</h3>
<h3>6. 用户-注册-控制器层</h3>
<h3>7. 用户-注册-前端页面</h3>
<h3>8. 用户-登录-持久层</h3>
<h3>9. 用户-登录-业务层</h3>
<h3>10. 用户-登录-控制器层</h3>
<h3>11. 用户-登录-前端页面</h3>
<h3>------------------------------------</h3>
<h3>附1：关于VARCHAR的设置</h3>
<p>在MySQL数据库中，如果某字段的类型设置为<code>VARCHAR(30)</code>，表示访字段最多存储30个字节长度的数据，如果尝试存入的数据超出30字节，多余的部分将不会被存储！</p>
<p>30个字节，表示为多少个字符，取决于使用的字符编码！</p>
<p>以UTF8为例，如果存储的内容是1个字节的，存储下来例如：</p>
<pre><code>0110 0001
</code></pre>

<p>其规则是使用第1位0表示该字符只占1个字节，剩余的7位表示1个字符！</p>
<p>如果某个字符需要使用2个字节才可以表示，UTF8的规则表现例如：</p>
<pre><code>110 01010   10 000111
</code></pre>

<p>即第1个字节使用110作为前缀，表示该字符需要2个字节，其中，这里的2个1就表示共要2个字节，后续的第2个字节中的10就表示它是跟随前序字节一起的，第2个字节中的剩余6位才是编码位。</p>
<p>在2个字节中，只有5+6=11个编码位，只能表示2048种不同的组合，所以，在UTF8编码中，2个字节是不可以表示中文的，因为中文里的汉字数量及相关符号远超2048个！所以，中文需要3个字节来表示，在UTF8中，3字节的编码例如：</p>
<pre><code>1110 0101   10 101010   10 010101
</code></pre>

<p>在UTF8中，还有需要4字节才可以表示的编码……</p>
<p>所以，如果设计的是<code>VARCHAR(30)</code>，并使用UTF8编码，到底可以存入多少字符，也是不确定的，如果存的是ASCII字符，最多可以存30个，如果存的是汉字，只能计划为10个，如果是混合的，可以计划为例如15个英文加上5个汉字……所以，通常，在设置时，只能根据最大需求来进行设计！</p>
<p>另外，请还注意一个问题：设置的字节值，并不代码存储占用空间！例如将<code>VARCHAR(30)</code>存满，还另外会占用1个字节存储长度，表示存了多少字节的数据，在存储长度时，数据类型的字节数在255个时是分隔线，如果使用<code>VARCHAR</code>设置的字节数超过255，则另外需要占用2个字节存储长度！例如设置<code>VARCHAR(300)</code>，则额外的存储长度时使用字节数就是2个字节！</p>

</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
